#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import json
import time

from exchange import log
from exchange.commons.command import Commands
from exchange.commons.common import get_key_value
from exchange.commons.exchange_exception import ExchangeInternalException
from exchange.commons.powershell import execute, execute_without_auth, get_hostname, execute_with_output_bool, \
    execute_with_depth, execute_without_timeout
from exchange.commons.const import RETRY_COUNT, RETRY_WAIT_SECONDS


# exchange命令执行客户端
class ExchangeCmdClient:
    # 构造方法
    def __init__(self, p_id):
        ret, username = get_key_value(f"job_targetEnv_auth_authKey_{p_id}")
        ret, password = get_key_value(f"job_targetEnv_auth_authPwd_{p_id}")
        self.__username = username
        self.__password = password

    # 获取exchange server运行状态
    @staticmethod
    def get_exchange_online_status():
        # 执行在线检查命令 Get-Service "MSExchangeIS" -ErrorAction SilentlyContinue|Select Status
        result = execute_without_auth(Commands.GET_EXCHANGE_ONLINE_STATUS)
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange online status fail code: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange server在线状态结果
        return json.loads(result.get("Output"))["Status"]

    # 获取exchange server版本
    @staticmethod
    def get_exchange_version():
        # 执行命令获取Microsoft Exchange Server版本 Get-Package -Name "Microsoft Exchange Server"|Select Version
        result = execute_without_auth(Commands.GET_EXCHANGE_VERSION)
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange version fail code: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange server版本
        return json.loads(result.get("Output"))["Version"]

    # 获取exchange database guid
    def get_exchange_database_guid(self, database_name: str):
        # 执行命令获取Guid Get-MailboxDatabase -Identity database_name | Select Guid
        result = execute(self.__username, self.__password,
                         Commands.GET_EXCHANGE_DATABASE_GUID.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange database guid fail code: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange guid
        return json.loads(result.get("Output"))["Guid"]

    # 设置数据库属性允许覆盖恢复
    def set_exchange_database_allow_file_restore(self, database_name: str):
        # 执行命令设置AllowFileRestore Set-MailboxDatabase -identity DatabaseName -AllowFileRestore $true
        result = execute(self.__username, self.__password,
                         Commands.SET_EXCHANGE_ALLOW_FILE_RESTORE_TRUE.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Set exchange database AllowFileRestore code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))

    # 获取数据库所属服务名称
    def get_exchange_database_server_name(self, database_name: str):
        # 执行命令获取Server Get-MailboxDatabase -Identity DatabaseName | Select Server
        result = execute(self.__username, self.__password,
                         Commands.GET_EXCHANGE_DATABASE_SERVER.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange database server fail code: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange server name
        return json.loads(result.get("Output"))["Server"]["Name"]

    # 获取数据库所有副本信息
    def get_exchange_database_copies(self, copy_name: str):
        # 执行命令获取数据库所有副本 Get-MailboxDatabaseCopyStatus -Identity CopyName | Select Name,Status
        result = execute(self.__username, self.__password,
                         Commands.GET_EXCHANGE_DATABASE_COPIES.format(copy_name=copy_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange database copies fail code: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange copy信息
        result_obj = json.loads(result.get("Output"))
        if not isinstance(result_obj, list):
            result_obj = [result_obj]
        return result_obj

    # 暂停被动副本复制和激活
    def suspend_passive_copy(self, copy_name: str):
        # 执行命令暂停被动副本复制
        # Suspend-MailboxDatabaseCopy -Identity CopyName -Confirm:$False
        result = execute(self.__username, self.__password,
                         Commands.SUSPEND_EXCHANGE_DATABASE_COPY.format(database_copy=copy_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Suspend exchange database passive copy fail code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))

    # 设置种子并恢复被动副本复制
    def update_passive_copy_seed(self, copy_name: str):
        # 执行命令恢复被动副本复制 Update-MailboxDatabaseCopy -Identity '{database_copy}'
        # -DatabaseOnly -DeleteExistingFiles -Force -Confirm:$False
        result = execute_without_timeout(self.__username, self.__password,
                                         Commands.UPDATE_EXCHANGE_DATABASE_COPY.format(database_copy=copy_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Resume exchange database passive copy fail code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))

    # 卸载数据库
    def dismount_exchange_database(self, database_name: str):
        # 执行命令卸载数据库 Dismount-Database - Identity DatabaseName - Confirm:$false
        result = execute(self.__username, self.__password,
                         Commands.DISMOUNT_EXCHANGE_DATABASE.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Umount exchange database fail code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))

    # 挂载数据库
    def mount_exchange_database(self, database_name: str):
        # 执行命令卸载数据库 Dismount-Database - Identity DatabaseName - Confirm:$false
        retry_count = 0
        while True:
            result = execute_without_timeout(self.__username, self.__password,
                                             Commands.MOUNT_EXCHANGE_DATABASE.format(database_name=database_name))
            if result.get("ReturnCode", "") == "0":
                log.info(f"The exchange database was successfully mounted: {database_name}.")
                return
            # 命令执行失败抛出异常,记录日志抛出异常
            retry_count += 1
            log.info(f"Mount retry count: {retry_count}.")
            if retry_count >= RETRY_COUNT:
                log.error("Mount exchange database fail code: %s, ErrorMessage: %s",
                             result.get("ReturnCode", ""),
                             result.get("Output", ""))
                raise ExchangeInternalException(result.get("Output", ""))
            time.sleep(RETRY_WAIT_SECONDS)

    # 获取edb文件路径和log路径
    def get_exchange_database_edb_and_log_path(self, database_name: str):
        # 执行命令获取数据库edb文件和log路径 Get-MailboxDatabase -Identity DatabaseName | Select-Object EdbFilePath, LogFolderPath
        result = execute(self.__username, self.__password,
                         Commands.GET_EXCHANGE_DATABASE_LOG_AND_EDB_PATH.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange database edb and log path fail code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange edb文件和log路径
        result_obj = json.loads(result.get("Output"))
        return result_obj["EdbFilePath"]["PathName"], result_obj["LogFolderPath"]["PathName"]

    # 获取log日志前缀
    def get_exchange_database_log_prefix(self, database_name: str):
        # 执行命令获取数据库log日志前缀 Get-MailboxDatabase -Identity DatabaseName |Select-object LogFilePrefix
        result = execute(self.__username, self.__password,
                         Commands.GET_EXCHANGE_DATABASE_LOG_FILE_PREFIX.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error("Get exchange database log file prefix fail code: %s, ErrorMessage: %s",
                         result.get("ReturnCode", ""),
                         result.get("Output", ""))
            raise ExchangeInternalException(result.get("Output", ""))
        # 获取exchange log日志前缀
        return json.loads(result.get("Output"))["LogFilePrefix"]

    # 创建RDB数据库
    def create_rdb_database(self, database_name: str, edb_file_path: str, log_folder_path: str):
        host_name_result = get_hostname()
        if host_name_result.get("ReturnCode") != "0":
            log.error(f'Get host name failed when create RDB database, code: '
                         f'{host_name_result.get("ReturnCode", "")}, output: {host_name_result.get("Output", "")}')
            raise ExchangeInternalException(host_name_result.get("Output", ""))
        server = host_name_result.get("Output")
        result = execute(self.__username, self.__password,
                         Commands.CREATE_RDB_DATABASE.format(database_name=database_name,
                                                             server=server,
                                                             edb_file_path=edb_file_path,
                                                             log_folder_path=log_folder_path))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'create RDB database failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return True

    # 恢复邮箱，返回创建的请求的Guid
    def restore_mailbox(self, request_name, database_name, source_mailbox, target_mailbox):
        result = execute(self.__username, self.__password,
                         Commands.MAILBOX_RESTORE_REQUEST.format(request_name=request_name,
                                                                 database_name=database_name,
                                                                 source_mailbox=source_mailbox,
                                                                 target_mailbox=target_mailbox))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'create RDB database failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return json.loads(result.get("Output"))["RequestGuid"]

    # 删除数据库
    def remove_database(self, database_name):
        result = execute(self.__username, self.__password,
                         Commands.REMOVE_DATABASE.format(database_name=database_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'remove RDB database failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return True

    # 删除邮箱恢复请求
    def remove_mailbox_restore_request(self, request_id):
        result = execute(self.__username, self.__password,
                         Commands.REMOVE_MAILBOX_RESTORE_REQUEST.format(request_id=request_id))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'remove mailbox restore request failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return True

    # 查询恢复请求状态
    def get_mailbox_restore_request_status(self, request_name):
        result = execute(self.__username, self.__password,
                         Commands.GET_MAILBOX_RESTORE_REQUEST.format(request_name=request_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'remove mailbox restore request failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return json.loads(result.get("Output"))

    # 查询数据库状态
    def get_database_status(self, db_name):
        result = execute(self.__username, self.__password,
                         Commands.GET_MAILBOX_DATABASE_COPY_STATUS.format(db_name=db_name))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'get_database_status request failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return json.loads(result.get("Output"))

    # 查询节点的数据库
    def get_database_in_server(self):
        host_name_result = get_hostname()
        if host_name_result.get("ReturnCode") != "0":
            log.error(f'Get host name failed when get database, code: '
                         f'{host_name_result.get("ReturnCode", "")}, output: {host_name_result.get("Output", "")}')
            raise ExchangeInternalException(host_name_result.get("Output", ""))
        result = execute(self.__username, self.__password,
                         Commands.GET_DATABASE_IN_SERVER.format(server=host_name_result))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'get database in server: {host_name_result} request failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return json.loads(result.get("Output"))

    # 检测邮箱是否存在
    def check_exchange_mailbox_exist(self, mailbox_name):
        log.info(f"check_exchange_mailbox_exist start, mailbox_name:{mailbox_name}.")
        result = execute_with_output_bool(self.__username, self.__password,
                                          Commands.IS_MAILBOX_EXIST.format(mailbox_name=mailbox_name))
        if result.get("Output") == "True":
            return True
        else:
            log.error("check_exchange_mailbox_exist ReturnCode: %s, ErrorMessage: %s", result.get("ReturnCode", ""),
                         result.get("Output", ""))
            return False

    # 创建exchange数据库
    def create_exchange_database(self, database_name, server_name, edb_path, log_path):
        result = execute_with_output_bool(self.__username, self.__password,
                                          Commands.CREATE_EXCHANGE_DATABASE.format(database_name=database_name,
                                                                                   server_name=server_name,
                                                                                   edb_path=edb_path,
                                                                                   log_path=log_path))
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'create exchange database failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))

    # 查询所有节点的数据库
    def get_all_database_copies(self):
        log.info(f"start get all exchange database.")
        result = execute_with_depth(self.__username, self.__password,
                                    Commands.GET_ALL_EXCHANGE_DATABASE_COPIES, 2)
        # 命令执行失败抛出异常,记录日志抛出异常
        if result.get("ReturnCode", "") != "0":
            log.error(f'get all database failed, code: '
                         f'{result.get("ReturnCode", "")}, output: {result.get("Output", "")}')
            raise ExchangeInternalException(result.get("Output", ""))
        return json.loads(result.get("Output"))
